﻿
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Workflow.ComponentModel;
using System.Workflow.Activities;

using TextFlow.Activities;
using System.Linq.Expressions;

namespace TextFlow.Engine
{
	public class TextFlowDecompiler
	{
        private const string NameTemplate = @"<>___{0}";

        public static string Decompile( TextFlowWorkflow workflow )
        {
            if ( workflow == null )
            {
                throw new ArgumentException( "workflow" );
            }

            WorkflowVisitor visitor = new WorkflowVisitor();

            StringBuilder buffer = new StringBuilder();
            StringBuilder prefix = new StringBuilder();

            Action<string> write = ( s ) => buffer.AppendFormat( "{0}{1}\r\n\r\n", prefix.ToString(), s );

            bool branchFound = false;

            visitor.Pre += delegate( object sender, VisitActivityEventArgs e )
            {
                string type = e.Activity.GetType().Name;

                switch ( type )
                {
                    case "DelayActivity":

                        DelayActivity delay = e.Activity as DelayActivity;

                        write( string.Format( "WAITFOR {0} SECONDS", delay.TimeoutDuration.TotalSeconds.ToString() ) );

                        break;

                    case "StoreVariableActivity":

                        StoreVariableActivity sva = e.Activity as StoreVariableActivity;

                        write( string.Format( "SET {0} = {1}", sva.LHS, sva.RHS.ToOutputString() ) );

                        break;

                    case "ParallelActivity":

                        write( "INPARALLEL" );

                        break;

                    case "WhileActivity":

                        WhileActivity wa = e.Activity as WhileActivity;

                        TextFlowCondition cond1 = wa.Condition as TextFlowCondition;

                        write( string.Format( "WHILE {0}", cond1.EvalExpr.ToOutputString() ) );

                        break;

                    case "IfElseActivity":

                        branchFound = false;

                        break;

                    case "IfElseBranchActivity":

                        IfElseBranchActivity branch = e.Activity as IfElseBranchActivity;

                        TextFlowCondition cond2 = branch.Condition as TextFlowCondition;

                        if ( cond2 == null )
                        {
                            write( "ELSE\r\n" );
                        }
                        else
                        {
                            if ( branchFound == false )
                            {
                                branchFound = true;

                                write( string.Format( "IF\r\n\t{0}\r\nTHEN", cond2.EvalExpr.ToOutputString() ) );
                            }
                            else
                            {
                                write( string.Format( "ELSEIF\r\n\t{0}\r\nTHEN", cond2.EvalExpr.ToOutputString() ) );
                            }
                        }

                        break;

                    case "TextFlowWorkflow":
                        break;

                    case "SequenceActivity":

                        if ( e.Activity.Parent is ParallelActivity )
                        {
                            write( "START" );
                        }

                        break;

                    default:

                        write( string.Format( "[{0}]", ActivityFactory.GetIdentifier( e.Activity.GetType() ) ) );

                        break;
                }

                prefix.Append( "\t" );
            };

            visitor.Post += delegate( object sender, VisitActivityEventArgs e )
            {
                prefix.Remove( prefix.Length - 1, 1 );

                string type = e.Activity.GetType().Name;

                switch ( type )
                {
                    case "WhileActivity":

                        write( "ENDWHILE" );

                        break;

                    case "IfElseActivity":

                        write( "ENDIF" );

                        break;

                    case "ParallelActivity":

                        write( "ENDPARALLEL" );

                        break;

                    case "SequenceActivity":

                        if ( e.Activity.Parent is ParallelActivity )
                        {
                            write( "END" );
                        }

                        break;

                    default:

                        break;
                }
            };

            visitor.Visit( workflow );

            return buffer.ToString();
        }
	}

    internal static class ExpressionHelper
    {
        public static string ToOutputString( this Expression expr )
        {
            if ( expr is MethodCallExpression )
            {
                return ( expr as MethodCallExpression ).Arguments.First().ToString().Trim( '"' );
            }
            else if ( expr is BinaryExpression )
            {
                BinaryExpression binary = expr as BinaryExpression;

                string normal = binary.ToString();

                normal = normal.Replace( binary.Left.ToString(), binary.Left.ToOutputString() );

                normal = normal.Replace( binary.Right.ToString(), binary.Right.ToOutputString() );

                return normal;
            }
            else
            {
                return expr.ToString();
            }
        }
    }

    internal class VisitActivityEventArgs : EventArgs
    {
        public Activity Activity { get; private set; }

        public VisitActivityEventArgs( Activity activity )
        {
            Activity = activity;
        }
    }

    internal class WorkflowVisitor
    {
        public WorkflowVisitor()
        {
        }

        public event EventHandler<VisitActivityEventArgs> Pre;
        public event EventHandler<VisitActivityEventArgs> Post;

        public void Visit( Activity graph )
        {
            if ( graph == null )
            {
                throw new ArgumentNullException( "graph" );
            }

            if ( Pre != null )
            {
                Pre( this, new VisitActivityEventArgs( graph ) );
            }

            if ( graph is CompositeActivity )
            {
                foreach ( Activity child in ( graph as CompositeActivity ).Activities )
                {
                    Visit( child );
                }
            }

            if ( Post != null )
            {
                Post( this, new VisitActivityEventArgs( graph ) );
            }
        }
    }
}
